--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.github/workflows/pypi.yml 7867034f5d7e21595f7481f75710a1246946b49f (7867034f) Text, 8.72 KB
T8b949e# Publish ``reticulum-meshchatx`` sdist and wheel to PyPI (Trusted Publishing).
T8b949e# Builds the Vite frontend and offline bundles via the reusable frontend workflow,
T8b949e# places them under ``meshchatx/public/``, then runs ``python -m build``.
T8b949e#
T8b949e# Tag runs also emit SLSA generic provenance (``generator_generic_slsa3``) for the
T8b949e# exact ``dist/`` digests and optional Cosign ``*.cosign.bundle`` files next to each
T8b949e# distribution (same scripts as ``build-release.yml``; skips if ``COSIGN_PRIVATE_KEY`` unset).
T8b949e# PyPI upload uses a staging directory so bundles are not sent to the index.
T8b949e#
T8b949e# PyPI Trusted Publisher must reference this file as ``pypi.yml`` and use the
T8b949e# ``pypi`` GitHub Environment (with required reviewers if you enabled protection).
T8b949e#
T8b949e# Pinned first-party actions (bump tag and SHA together when upgrading):
T8b949e# actions/checkout@v6.0.1 8e8c483db84b4bee98b60c0593521ed34d9990e8
T8b949e# actions/setup-python@v6.2.0 a309ff8b426b58ec0e2a45f0f869d46889d02405
T8b949e# actions/upload-artifact@v5.0.0 330a01c490aca151604b8cf639adc76d48f6c5d4
T8b949e# actions/download-artifact@v5.0.0 634f93cb2916e3fdff6788551b99b062d0335ce0
T8b949e#
T8b949e# SLSA generator (must stay @vX.Y.Z semver per upstream):
T8b949e# slsa-framework/slsa-github-generator/generator_generic_slsa3.yml@v2.1.0
T8b949e#
T8b949e# Third-party pin (resolve before bumping ``release/v1``):
T8b949e# curl -sS "https://api.github.com/repos/pypa/gh-action-pypi-publish/commits/release/v1" | jq -r '.sha'
T8b949e# pypa/gh-action-pypi-publish@release/v1 -> cef221092ed1bacb1cc03d23a2d87d1d172e277b
Tff7b72nameTb4b4b4: Ta5d6ffPublishTa5d6ff Ta5d6fftoTa5d6ff Ta5d6ffPyPI
Tff7b72onTb4b4b4:
Tff7b72pushTb4b4b4:
Tff7b72tagsTb4b4b4:
Tb4b4b4- Ta5d6ff"Ta5d6ffv*Ta5d6ff"
Tb4b4b4- Ta5d6ff"Ta5d6ff!v*-*Ta5d6ff"
Tff7b72workflow_dispatchTb4b4b4:
Tff7b72permissionsTb4b4b4:
Tff7b72contentsTb4b4b4: Ta5d6ffread
Tff7b72actionsTb4b4b4: Ta5d6ffread
Tff7b72id-tokenTb4b4b4: Ta5d6ffwrite
Tff7b72concurrencyTb4b4b4:
Tff7b72groupTb4b4b4: Ta5d6ffpypi-${{Ta5d6ff Ta5d6ffgithub.workflowTa5d6ff Ta5d6ff}}-${{Ta5d6ff Ta5d6ffgithub.refTa5d6ff Ta5d6ff}}
Tff7b72cancel-in-progressTb4b4b4: Ta5d6fftrue
Tff7b72envTb4b4b4:
Tff7b72FORCE_JAVASCRIPT_ACTIONS_TO_NODE24Tb4b4b4: Ta5d6fftrue
Tff7b72NODE_OPTIONSTb4b4b4: Ta5d6ff--max-old-space-size=8192
Tff7b72PYTHON_VERSIONTb4b4b4: Ta5d6ff"Ta5d6ff3.14Ta5d6ff"
Tff7b72COSIGN_VERSIONTb4b4b4: Ta5d6ff"Ta5d6ff3.0.6Ta5d6ff"
Tff7b72jobsTb4b4b4:
Tff7b72frontendTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffBuildTa5d6ff Ta5d6fffrontendTa5d6ff Ta5d6ffartifact
Tff7b72usesTb4b4b4: Ta5d6ff./.github/workflows/frontend-build.yml
Tff7b72permissionsTb4b4b4:
Tff7b72contentsTb4b4b4: Ta5d6ffread
Tff7b72withTb4b4b4:
Tff7b72artifact_nameTb4b4b4: Ta5d6ffmeshchatx-frontend-pypi-${{Ta5d6ff Ta5d6ffgithub.run_idTa5d6ff Ta5d6ff}}-${{Ta5d6ff Ta5d6ffgithub.run_attemptTa5d6ff Ta5d6ff}}
Tff7b72retention_daysTb4b4b4: Ta5d6ff2
Tff7b72buildTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffBuildTa5d6ff Ta5d6ffPythonTa5d6ff Ta5d6ffdistributions
Tff7b72needsTb4b4b4: Ta5d6fffrontend
Tff7b72runs-onTb4b4b4: Ta5d6ffubuntu-latest
Tff7b72timeout-minutesTb4b4b4: Ta5d6ff30
Tff7b72outputsTb4b4b4:
Tff7b72hashesTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.slsa-hashes.outputs.hashesTa5d6ff Ta5d6ff}}
Tff7b72defaultsTb4b4b4:
Tff7b72runTb4b4b4:
Tff7b72shellTb4b4b4: Ta5d6ffbash
Tff7b72envTb4b4b4:
Tff7b72FRONTEND_ARTIFACT_NAMETb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffneeds.frontend.outputs.artifact_nameTa5d6ff Ta5d6ff}}
Tff7b72stepsTb4b4b4:
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCheckout
Tff7b72usesTb4b4b4: Ta5d6ffactions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
Tff7b72withTb4b4b4:
Tff7b72persist-credentialsTb4b4b4: Ta5d6fffalse
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffSetTa5d6ff Ta5d6ffupTa5d6ff Ta5d6ffPython
Tff7b72usesTb4b4b4: Ta5d6ffactions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
Tff7b72withTb4b4b4:
Tff7b72python-versionTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffenv.PYTHON_VERSIONTa5d6ff Ta5d6ff}}
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffDownloadTa5d6ff Ta5d6fffrontendTa5d6ff Ta5d6ffartifact
Tff7b72usesTb4b4b4: Ta5d6ffactions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
Tff7b72withTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffenv.FRONTEND_ARTIFACT_NAMETa5d6ff Ta5d6ff}}
Tff7b72pathTb4b4b4: Ta5d6ffmeshchatx/public
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffVerifyTa5d6ff Ta5d6fffrontendTa5d6ff Ta5d6ffbundleTa5d6ff Ta5d6ffinTa5d6ff Ta5d6fftree
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72set -euo pipefail
Tff7b72test -f meshchatx/public/index.html
Tff7b72test -d meshchatx/public/assets
Tff7b72test -d meshchatx/public/reticulum-docs-bundled/current
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffInstallTa5d6ff Ta5d6ffbuild
Tff7b72runTb4b4b4: Ta5d6ffpythonTa5d6ff Ta5d6ff-mTa5d6ff Ta5d6ffpipTa5d6ff Ta5d6ffinstallTa5d6ff Ta5d6ff-UTa5d6ff Ta5d6ffpipTa5d6ff Ta5d6ff"build>=1.2.0"
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffBuildTa5d6ff Ta5d6ffsdistTa5d6ff Ta5d6ffandTa5d6ff Ta5d6ffwheel
Tff7b72runTb4b4b4: Ta5d6ffpythonTa5d6ff Ta5d6ff-mTa5d6ff Ta5d6ffbuild
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffVerifyTa5d6ff Ta5d6ffwheelTa5d6ff Ta5d6ffcontainsTa5d6ff Ta5d6fffrontendTa5d6ff Ta5d6ffassets
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72set -euo pipefail
Tff7b72whl=(dist/*.whl)
Tff7b72if [[ "${#whl[@]}" -ne 1 ]]; then
Tff7b72echo "Expected exactly one wheel in dist/" >&2
Tff7b72ls -la dist >&2
Tff7b72exit 1
Tff7b72fi
Tff7b72export WHEEL_PATH="${whl[0]}"
Tff7b72python - <<'PY'
Tff7b72import os
Tff7b72import sys
Tff7b72import zipfile
Tff7b72path = os.environ["WHEEL_PATH"]
Tff7b72with zipfile.ZipFile(path) as zf:
Tff7b72names = zf.namelist()
Tff7b72if "meshchatx/public/index.html" not in names:
Tff7b72print("missing: meshchatx/public/index.html", file=sys.stderr)
Tff7b72sys.exit(1)
Tff7b72if not any(n.startswith("meshchatx/public/assets/") for n in names):
Tff7b72print("missing: meshchatx/public/assets/", file=sys.stderr)
Tff7b72sys.exit(1)
Tff7b72print("wheel contains meshchatx/public frontend paths")
Tff7b72PY
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffSLSATa5d6ff Ta5d6ffsubjectTa5d6ff Ta5d6ffhashesTa5d6ff Ta5d6ff(PyPITa5d6ff Ta5d6ffdists)
Tff7b72idTb4b4b4: Ta5d6ffslsa-hashes
Tff7b72ifTb4b4b4: Ta5d6ffgithub.ref_typeTa5d6ff Ta5d6ff==Ta5d6ff Ta5d6ff'tag'
Tff7b72runTb4b4b4: Ta5d6ffbashTa5d6ff Ta5d6ffscripts/ci/github-slsa-hashes-dist.sh
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCosignTa5d6ff Ta5d6ffattestationsTa5d6ff Ta5d6ff(sdistTa5d6ff Ta5d6ff+Ta5d6ff Ta5d6ffwheels)
Tff7b72ifTb4b4b4: Ta5d6ffgithub.ref_typeTa5d6ff Ta5d6ff==Ta5d6ff Ta5d6ff'tag'
Tff7b72envTb4b4b4:
Tff7b72COSIGN_PRIVATE_KEYTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.COSIGN_PRIVATE_KEYTa5d6ff Ta5d6ff}}
Tff7b72COSIGN_PASSWORDTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.COSIGN_PASSWORDTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_SERVER_URLTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.server_urlTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_REPOSITORYTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.repositoryTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_SHATb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.shaTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_REFTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.refTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_RUN_IDTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.run_idTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_RUN_ATTEMPTTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.run_attemptTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_WORKFLOWTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.workflowTa5d6ff Ta5d6ff}}
Tff7b72GITHUB_WORKFLOW_FILETb4b4b4: Ta5d6ffpypi.yml
Tff7b72COSIGN_VERSIONTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffenv.COSIGN_VERSIONTa5d6ff Ta5d6ff}}
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72set -eu
Tff7b72if [ -z "${COSIGN_PRIVATE_KEY:-}" ]; then
Tff7b72echo "Skipping cosign attestations (no COSIGN_PRIVATE_KEY)."
Tff7b72exit 0
Tff7b72fi
Tff7b72sh scripts/ci/setup-cosign.sh "${COSIGN_VERSION}"
Tff7b72printf '%s\n' "$COSIGN_PRIVATE_KEY" > /tmp/cosign.key
Tff7b72chmod 600 /tmp/cosign.key
Tff7b72export COSIGN_KEY_PATH=/tmp/cosign.key
Tff7b72sh scripts/ci/attest-release-assets.sh ./dist
Tff7b72rm -f /tmp/cosign.key
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffStoreTa5d6ff Ta5d6ffdistributionTa5d6ff Ta5d6ffpackages
Tff7b72usesTb4b4b4: Ta5d6ffactions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
Tff7b72withTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffpython-package-distributions
Tff7b72pathTb4b4b4: Ta5d6ffdist/
Tff7b72if-no-files-foundTb4b4b4: Ta5d6fferror
Tff7b72slsa-provenance-pypiTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffSLSATa5d6ff Ta5d6ffprovenanceTa5d6ff Ta5d6ff(PyPITa5d6ff Ta5d6ffdists)
Tff7b72needsTb4b4b4: Ta5d6ffbuild
Tff7b72ifTb4b4b4: Ta5d6ffgithub.ref_typeTa5d6ff Ta5d6ff==Ta5d6ff Ta5d6ff'tag'
Tff7b72permissionsTb4b4b4:
Tff7b72id-tokenTb4b4b4: Ta5d6ffwrite
Tff7b72contentsTb4b4b4: Ta5d6ffwrite
Tff7b72actionsTb4b4b4: Ta5d6ffread
Tff7b72usesTb4b4b4: Ta5d6ffslsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
Tff7b72withTb4b4b4:
Tff7b72base64-subjectsTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffneeds.build.outputs.hashesTa5d6ff Ta5d6ff}}
Tff7b72upload-assetsTb4b4b4: Ta5d6fffalse
Tff7b72provenance-nameTb4b4b4: Ta5d6ffmeshchatx-pypi-${{Ta5d6ff Ta5d6ffgithub.ref_nameTa5d6ff Ta5d6ff}}.intoto.jsonl
Tff7b72publish-to-pypiTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffPublishTa5d6ff Ta5d6fftoTa5d6ff Ta5d6ffPyPI
Tff7b72ifTb4b4b4: Tb4b4b4>Tb4b4b4-
Tff7b72github.ref_type == 'tag' &&
Tff7b72needs.build.result == 'success' &&
Tff7b72needs.slsa-provenance-pypi.result == 'success'
Tff7b72needsTb4b4b4:
Tb4b4b4- Ta5d6ffbuild
Tb4b4b4- Ta5d6ffslsa-provenance-pypi
Tff7b72runs-onTb4b4b4: Ta5d6ffubuntu-latest
Tff7b72timeout-minutesTb4b4b4: Ta5d6ff15
Tff7b72environmentTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffpypi
Tff7b72urlTb4b4b4: Ta5d6ffhttps://pypi.org/project/meshchatx/
Tff7b72permissionsTb4b4b4:
Tff7b72actionsTb4b4b4: Ta5d6ffread
Tff7b72contentsTb4b4b4: Ta5d6ffread
Tff7b72id-tokenTb4b4b4: Ta5d6ffwrite
Tff7b72stepsTb4b4b4:
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffDownloadTa5d6ff Ta5d6ffdistributions
Tff7b72usesTb4b4b4: Ta5d6ffactions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0
Tff7b72withTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffpython-package-distributions
Tff7b72pathTb4b4b4: Ta5d6ffdist/
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffStageTa5d6ff Ta5d6ffPyPITa5d6ff Ta5d6ffuploadsTa5d6ff Ta5d6ff(excludeTa5d6ff Ta5d6ffCosignTa5d6ff Ta5d6ffbundles)
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72set -euo pipefail
Tff7b72mkdir -p pypi-upload
Tff7b72shopt -s nullglob
Tff7b72files=(dist/*.whl dist/*.tar.gz)
Tff7b72if [[ "${#files[@]}" -eq 0 ]]; then
Tff7b72echo "No .whl or .tar.gz in dist/" >&2
Tff7b72ls -la dist >&2
Tff7b72exit 1
Tff7b72fi
Tff7b72cp -v "${files[@]}" pypi-upload/
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffPublishTa5d6ff Ta5d6fftoTa5d6ff Ta5d6ffPyPI
Tff7b72usesTb4b4b4: Ta5d6ffpypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b
Tff7b72withTb4b4b4:
Tff7b72packages-dirTb4b4b4: Ta5d6ffpypi-upload/
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────